#include <iostream>
#include <cerrno>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cstring>
#include <string>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
void Usage(std::string port)
{
    std::cout << port << " port" << std::endl;
}
void Service(int new_sock)
{
    // 提供服务
    while (true)
    {
        char buffer[1024];
        memset(buffer, 0, sizeof(buffer));
        ssize_t s = read(new_sock, buffer, sizeof(buffer) - 1);
        if (s > 0)
        {
            buffer[s] = 0;
            std::cout << "client#" << buffer << std::endl;
            std::string echo_string = "server echo#";
            echo_string += buffer;
            write(new_sock, echo_string.c_str(), echo_string.size());
        }
        else if (s == 0)
        {
            std::cout << "client quit ..." << std::endl;
            break;
        }
        else
        {
            std::cerr << "read fail" << errno << std::endl;
            break;
        }
    }
}
void* Handler(void* args)
{
    pthread_detach(pthread_self());
    int sock = *(int*)args;
    delete (int*)args;
    Service(sock);
    close(sock);
}
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }
    // 1创建套接字
    int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_sock < 0)
    {
        std::cerr << "socket create:" << errno << std::endl;
        return 2;
    }
    // 2 bind
    struct sockaddr_in local;
    memset(&local, 0, sizeof(local));
    local.sin_family = AF_INET;
    local.sin_port = htons(atoi(argv[1]));
    local.sin_addr.s_addr = INADDR_ANY;
    if (bind(listen_sock, (struct sockaddr *)&local, sizeof(local)) < 0)
    {
        std::cerr << "bind fail" << std::endl;
        return 3;
    }

    // 3 监听
    const int back_log = 5;
    if (listen(listen_sock, back_log) < 0)
    {
        std::cerr << "listen fail:" << errno << std::endl;
        return 4;
    }
    // signal(SIGCHLD, SIG_IGN); // Linux中父进程忽略子进程的SIGCHLD信号，子进程自动退出释放资源
    while (true)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int new_sock = accept(listen_sock, (struct sockaddr *)&peer, &len);
        if (new_sock < 0)
            continue;
        //得到连接的客户信息
        uint16_t cli_port = ntohs(peer.sin_port);
        std::string cli_ip = inet_ntoa(peer.sin_addr);

        std::cout << "get a new link ->" << cli_ip << ":" << cli_port << ":" << new_sock << std::endl;

        //多线程版
        pthread_t tid;
        int* pram = new int(new_sock);
        pthread_create(&tid,nullptr,Handler,pram);

        // //多进程版
        // pid_t id = fork();
        // if (id < 0)
        //     continue;
        // else if (id == 0)
        // {
        //     // child
        //     close(listen_sock);
        //     Service(new_sock);
        //     close(new_sock); //防止文件描述符泄露
        //     exit(0);
        // }
        // else
        // {
        //     // father do nothing
        //     close(new_sock);
        // }
        // //单进程版
        // Service(new_sock);
    }
    return 0;
}